feat: add per-operation auth descriptor with precedence ladder#155
Merged
Conversation
Auth requirements are per-operation, and a two-boolean "needs-auth / needs-key" model does not generalise to operations that accept several alternative schemes with different OAuth parameters. This adds a hand-constructable, scheme-agnostic descriptor model to sdk-core plus a deterministic resolver. - AuthRequirement: one accepted AuthScheme paired with its own OAuth scopes/params (immutable, Builder + newBuilder). - AuthDescriptor: a per-operation ordered list of AuthRequirements in preference order (immutable, Builder + newBuilder, of/ofSchemes factories). Records which schemes are acceptable, never how they are stamped onto the wire. - AuthDescriptorTier / AuthResolution: the precedence tier and the resolved outcome (requirement + tier + anonymous flag). - AuthDescriptorResolver: applies two precedence orders — tier precedence (per-call override > operation default > client default, no fall-through past a supplied higher tier) then requirement precedence within the chosen descriptor (first satisfiable scheme wins; NO_AUTH is always satisfiable). Throws AuthResolutionException naming the required and available schemes when nothing matches. The resolver stays scheme-agnostic: callers supply the set of schemes they can satisfy and map the resolved requirement to a concrete credential and auth step themselves; per-cloud / OAuth specifics stay in adapters. No code generation — these are the runtime primitives a generator would later target. Closes #63
Collapse the auth-requirement model onto a single type. AuthMetadata flattened an operation's schemes into one shared OAuth bag and was never wired into any pipeline path; AuthDescriptor/AuthRequirement supersede it with per-scheme OAuth parameters, so remove AuthMetadata and its test. Align AuthRequirement construction with AuthDescriptor: make the primary constructor private and expose an of(scheme, scopes, params) factory (@JvmStatic/@jvmoverloads) alongside the Builder, so the two sibling value types share one construction pattern. Defensively copy the OAuth scopes/params in AuthRequirement.Builder's setters so a source collection mutated between the setter call and build() cannot leak into the built instance, matching the copy-in discipline used elsewhere in the type. Update AuthScheme KDoc and the README package map to reference the descriptor model; regenerate the sdk-core API snapshot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auth requirements are per-operation, and a two-boolean "needs-auth / needs-key" model does not generalise to operations that accept several alternative schemes with different OAuth parameters. This adds a hand-constructable, scheme-agnostic auth descriptor model to
sdk-coreplus a deterministic resolver.What this adds (
org.dexpace.sdk.core.http.auth)AuthRequirement— one acceptedAuthSchemepaired with its own OAuth scopes/params. WhereAuthMetadataflattens an operations schemes into a single shared OAuth bag, a requirement pairs each scheme with its own parameters. Immutable, private-field copy-in +Builder/newBuilder,of(scheme)convenience.AuthDescriptor— the per-operation ordered list ofAuthRequirements in preference order. Immutable,Builder/newBuilder,of(...)/ofSchemes(...)factories,allowsAnonymous(). Records which schemes are acceptable and in what order, never how a scheme is stamped onto the wire.AuthDescriptorTier— the precedence tier an descriptor occupies:PER_CALL>OPERATION>CLIENT.AuthResolution— the resolved outcome: the chosen requirement, the tier it came from, and anisAnonymousflag.AuthResolutionException— tailored failure naming the required and available schemes (e.g.operation requires one of [OAUTH2, API_KEY] but no matching credential is available (have: [BASIC])).AuthDescriptorResolver— the precedence ladder. Two independent orders:NO_AUTHis always satisfiable (anonymous access); any other scheme is satisfiable iff it is in the caller-supplied available-schemes set.Scheme-agnostic by design
Core never inspects a concrete
Credentialor knows how a scheme is stamped. The caller supplies the set of schemes it can satisfy (derived from configured credentials) and maps the resolved requirement to a credential + auth step itself. Per-cloud / OAuth specifics stay in adapters. There is no code generation — these are the runtime primitives a generator would later target, fully usable by hand today.Tests
New suites cover requirement/descriptor construction + builders + defensive copies, tier precedence (including the no-fall-through rule), requirement precedence, anonymous (
NO_AUTH) handling, OAuth-parameter forwarding, and the tailored failure message.Gated build (scoped, run locally)
Closes #63
Revision — consolidated onto one model
AuthMetadata. It flattened an operation's schemes into a single shared OAuth bag, was never wired into a pipeline path, and is fully superseded byAuthRequirement/AuthDescriptor(each scheme paired with its own OAuth parameters). Keeping both would have left two parallel models with no bridge. Its test is removed, and theAuthSchemeKDoc and README package map now reference the descriptor model.AuthRequirementconstruction now matchesAuthDescriptor— the primary constructor is private, with anof(scheme, scopes, params)factory (@JvmStatic/@JvmOverloads) alongside theBuilder, so the two sibling value types share one construction pattern.AuthRequirement.Builder's OAuth setters defensively copy their argument, so a source collection mutated between the setter call andbuild()cannot leak into the built instance.:sdk-coretest / ktlintCheck / detekt / apiCheck all pass; the API snapshot is regenerated.